package com.llmm.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

/**
 * @description: 文件分块下载。
 * @author: llmm
 * @date: 2022/5/21
 */
@RestController
@RequestMapping("largeFile")
public class DownloadController {
    //
    private final static String utf8 = "utf-8";

    @GetMapping("download")
    public void downloadFile(HttpServletRequest request, HttpServletResponse response){
        File file = new File("D:\\paypro\\7、发送消息业务实现.mp4");
        response.setCharacterEncoding(utf8);
        // jdk8 try() 自动关闭IO流
        try (OutputStream os = new BufferedOutputStream(response.getOutputStream());
             InputStream is = new BufferedInputStream(new FileInputStream(file))) {
            // 分片下载。
            long fileSize =file.length();
            //需要设计媒体类型。(下载类型)
            //使客户端浏览器区分不同种类的数据，并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。
            response.setContentType("application/x-download");
            // 文件名
            String filename=URLEncoder.encode(file.getName(),utf8);
            //另存为
            response.addHeader("Content-Disposition", "attachment;fileName=" +filename);
            // 支撑分片下载。
            response.setHeader("Accept-Range","bytes");
            // 自定义 header 参数. 方便java客户端获取。
            response.setHeader("fSize",String.valueOf(fileSize));
            response.setHeader("fName",filename);

            // 记录读取位置，起始位，结束位。
            long pos = 0;
            long last = fileSize-1;
            long sum = 0;
            //判断是否要分片。 http 存在 Range bytes=100-1000
            if (null != request.getHeader("Range")){
                // 表示做分片下载。
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                // 取 bytes 的起始位置。 bytes=100-1000   bytes=100 （最后）
                String numRange = request.getHeader("Range").replaceAll("bytes=", "");
                String[] strRange = numRange.split("-");
                // 判断长度时2（起始，结束），还是1（到最后）
                if (strRange.length ==2){
                    pos = Long.parseLong(strRange[0].trim());
                    last = Long.parseLong(strRange[1].trim());
                    // 可能存在 最后一个 略大于文件大小。
                    if (last>fileSize-1){
                        last=fileSize-1;
                    }
                }else {
                    // 取开始的位置。
                    pos=Long.parseLong(numRange.replace("-","").trim());
                }
            }
            // 读了文件多少
            long rangLength = last -pos +1;
            // 文件信息。
            String contentRange = new StringBuffer("bytes ").append(pos).append("-").append(last).append("/").append(filename).toString();
            response.setHeader("Content-Range",contentRange);
            response.setHeader("Content-Length",String.valueOf(rangLength));
            // 初始化。

            // 跳过前面，从pos位置开始读取。
            is.skip(pos);
            byte[] buffer = new byte[1024];
            int length=0;
            while (sum<rangLength){
                length = is.read(buffer,0,((rangLength-sum) <= buffer.length ? ((int)(rangLength-sum)) :  buffer.length));
                sum = sum+ length;
                os.write(buffer,0,length);
            }
            System.out.println("成功");
        }catch (Exception e){
            e.printStackTrace();
        }




    }
}


// https://v.qq.com/x/page/h3253u19esf.html
